package webx.http;

import stdx.BaseException;
import stdx.Utils;
import webx.LogFile;
import stdx.Optional;
import stdx.Required;
import java.util.Set;
import stdx.BaseException;
import webx.json.JsonObject;

import java.lang.reflect.Field;

public class HttpRequest{
	byte[] body = null;
	String path = null;
	String param = null;
	HttpHeadNode head = new HttpHeadNode();
	HttpDataNode data = new HttpDataNode();
	
	public HttpRequest(){
	}
	public HttpRequest(byte[] msg){
		parse(msg);
	}
	public boolean parse(byte[] msg){
		int pos = 0;
		int end = -1;
		final byte r = '\r';
		final byte n = '\n';

		end = msg.length;

		while (true){
			while (pos < end){
				if (msg[pos] == r) break;
				++pos;
			}

			if (pos >= end || pos + 4 > end) return false;

			if (msg[pos + 1] == n && msg[pos + 2] == r && msg[pos + 3] == n){
				body = new byte[end - pos - 4];
				System.arraycopy(msg, pos + 4, body, 0, body.length);

				head.parse(new String(msg, 0, pos));

				try {
					data.parse(param = new String(body, Http.GetCharset()));
				}
				catch (Exception e){
					LogFile.Error(e);
					return false;
				}

				path = head.get(":path");

				return true;
			}

			pos++;
		}
	}

	public String getPath(){
		return path;
	}
	public byte[] getBody(){
		if (body == null){
			try{
				body = data.toString().getBytes(Http.GetCharset());
			}
			catch(Exception e){
				e.printStackTrace();
			}
		}

		return body;
	}
	public Set<String> getKeys(){
		return data.getKeys();
	}
	public String get(String key){
		return data.get(key);
	}
	public String getParamString(){
		return param;
	}
	public void set(String key, String val){
		data.set(key, val);
		body = null;
	}
	public <T> T toObject(Class<T> clazz) throws Exception{
		return toObject(clazz, true);
	}
	public <T> T toObject(Class<T> clazz, boolean checked) throws Exception{
		T obj = null;

		if (body == null || body.length <= 0){
			obj = clazz.getDeclaredConstructor().newInstance();

			if (checked) CheckSimpleField(obj);

			return obj;
		}

		String msg = new String(body, Http.GetCharset()).trim();
		Field[] fields = clazz.getFields();

		if (msg.startsWith("{") && msg.endsWith("}")){
			obj = new JsonObject(msg).toObject(clazz);
		}
		else{
			obj = clazz.getDeclaredConstructor().newInstance();

			for (Field field : fields) Utils.InitFieldValue(field, obj, get(field.getName()));
		}

		if (checked) CheckSimpleField(obj);

		return obj;
	}

	public String getSessionId(){
		String sid = getHeader("Cookie");

		if (Utils.IsNotEmpty(sid)){
			sid = sid.replaceAll(" ", "");

			int pos = sid.indexOf("sid=");

			if (pos >= 0){
				int end = sid.indexOf(";", pos += 4);

				return end > 0 ? sid.substring(pos, end) : sid.substring(pos);
			}
		}

		return get("sid");
	}
	public Set<String> getHeadKeys(){
		return head.getKeys();
	}
	public String getHeader(String key){
		return head.get(key);
	}
	public void setHeader(String key, String val){
		head.set(key, val);
	}

	public static void CheckSimpleField(Object obj) throws Exception{
		Field[] vec = obj.getClass().getFields();

		for (Field field : vec){
			int len = 0;
			Object val = null;
			String name = null;
			String regex = null;
			long[] range = null;
			long[] length = null;
			String[] option = null;
			Required required = field.getAnnotation(Required.class);

			if (required != null){
				if (Utils.IsEmpty(val = field.get(obj))){
					LogFile.Error("parameter[%s] missing", field.getName());
					throw BaseException.PARAMERR.copy("parameter[" + field.getName() + "] missing");
				}

				name = required.value();
				regex = required.regex();
				range = required.range();
				length = required.length();
				option = required.option();
			}
			else {
				if (Utils.IsEmpty(val = field.get(obj))) continue;

				Optional optional = field.getAnnotation(Optional.class);

				if (optional != null) {
					name = optional.value();
					regex = optional.regex();
					range = optional.range();
					length = optional.length();
					option = optional.option();
				}
			}

			if (name == null) continue;

			if ((len = range.length) > 0){
				try {
					long tmp = Long.parseLong(val.toString());

					while (--len >= 0){
						if (tmp == range[len]) break;
					}

					if (len < 0){
						LogFile.Error("parameter[%s] error", field.getName());
						throw BaseException.PARAMERR.copy("parameter[" + field.getName() + "] error");
					}
				}
				catch (Exception e){
					LogFile.Error("%s", Utils.GetStackString(e));
					LogFile.Error("parameter[%s] error", field.getName());
					throw BaseException.PARAMERR.copy("parameter[" + field.getName() + "] error");
				}
			}

			if ((len = option.length) > 0){
				String tmp = val.toString();

				while (--len >= 0){
					if (tmp.equals(option[len])) break;
				}

				if (len < 0){
					LogFile.Error("parameter[%s] error", field.getName());
					throw BaseException.PARAMERR.copy("parameter[" + field.getName() + "] error");
				}
			}

			if ((len = length.length) > 0){
				String tmp = val.toString();

				if (len >= 1){
					if (tmp.length() < length[0]){
						LogFile.Error("parameter[%s] error", field.getName());
						throw BaseException.PARAMERR.copy("parameter[" + field.getName() + "] error");
					}

					if (len >= 2 && tmp.length() > length[1]){
						LogFile.Error("parameter[%s] error", field.getName());
						throw BaseException.PARAMERR.copy("parameter[" + field.getName() + "] error");
					}
				}
			}

			if (Utils.IsNotEmpty(regex)){
				if (!val.toString().matches(regex)){
					LogFile.Error("parameter[%s] error", field.getName());
					throw BaseException.PARAMERR.copy("parameter[" + field.getName() + "] error");
				}
			}
		}
	}
}